
#include "RobotApplication.h"

#include <OgreException.h>
#include <OgreConfigFile.h>
#include <btBulletDynamicsCommon.h>

#include <sstream>


//-------------------------------------------------------------------------------------
RobotApp::RobotApp(void)
{
}
//-------------------------------------------------------------------------------------
RobotApp::~RobotApp(void)
{
	delete config;
	delete dispatcher;
	delete broadphase;
	delete solver;
	delete world;

	delete testSphere;
}
//-------------------------------------------------------------------------------------
void RobotApp::createScene(void)
{

	numPhysSteps = 5;


	config=new btDefaultCollisionConfiguration();
	dispatcher= new btCollisionDispatcher(config);
	broadphase = new btDbvtBroadphase();
	solver = new btSequentialImpulseConstraintSolver();

	world = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,config);
	world->setGravity(btVector3(0,-980,0));

	createCamera();
	createViewport();
	mCamNode->setPosition(Ogre::Vector3(1003,550,2116));

	sphereGenerator = new Ogre::NameGenerator("sphere");
	Ogre::Entity* sphereEnt = mSceneMgr->createEntity("sphere",Ogre::SceneManager::PT_CUBE);
	Ogre::Entity* sEnt = mSceneMgr->createEntity("realsphere",Ogre::SceneManager::PT_SPHERE);
	sphereEnt->setCastShadows(true);

	btTransform t;
	t.setIdentity();
	t.setOrigin(btVector3(1830,550,2116));
	btScalar mass = .1;
	btSphereShape* sphere = new btSphereShape(20);
	//btBoxShape* box = new btBoxShape(btVector3(10,10,10));
	btVector3 inertia(0,0,0);
	sphere->calculateLocalInertia(mass,inertia);
	btMotionState* motion = new btDefaultMotionState(t);
	btRigidBody::btRigidBodyConstructionInfo info(mass,motion,sphere,inertia);
	physicsBody = new btRigidBody(info);
	physicsBody->setRollingFriction(.5);
	world->addRigidBody(physicsBody);

	testSphere = new PhysicsObject(mSceneMgr,sEnt,physicsBody,.5,world);

	powerLevel = 900;
	for(int i=0; i<1000; ++i){
		fire();
	}
	powerLevel = 5000;
	Ogre::Vector3 lightdir(.35,-.2,.55);
	lightdir.normalise();

	Ogre::Light* light = mSceneMgr->createLight("sceneLight");
	light->setType(Ogre::Light::LT_DIRECTIONAL);
	light->setDirection(lightdir);
	Ogre::ColourValue color(1,.5,.4);
	//Ogre::ColourValue color(0,0,0);
	light->setDiffuseColour(color);
	light->setSpecularColour(color);

	mSceneMgr->setAmbientLight(Ogre::ColourValue(1,1,1));

	mTerrainGlobals = OGRE_NEW Ogre::TerrainGlobalOptions();

	mTerrainGroup = OGRE_NEW Ogre::TerrainGroup(mSceneMgr, Ogre::Terrain::ALIGN_X_Z, 513, 15000.0f);
	mTerrainGroup->setFilenameConvention(Ogre::String("BasicTutorial3Terrain"),Ogre::String("dat"));
	mTerrainGroup->setOrigin(Ogre::Vector3::ZERO);

	configureTerrainDefaults(light);

	for(long x=0; x<=0;++x)
		for(long y=0; y <= 0; ++y)
			defineTerrain(x,y);

	mTerrainGroup->loadAllTerrains(true);

	if(mTerrainsImported){
		Ogre::TerrainGroup::TerrainIterator ti = mTerrainGroup->getTerrainIterator();
		while(ti.hasMoreElements()){
			Ogre::Terrain* t = ti.getNext()->instance;

			initBlendMaps(t);
			bulletTerrain(t);
		}
	}

	mTerrainGroup->freeTemporaryResources();

	Ogre::ColourValue fadeColour(.1,.1,.1);
	mWindow->getViewport(0)->setBackgroundColour(fadeColour);

	//mSceneMgr->setFog(Ogre::FOG_LINEAR,fadeColour,0,10,150);



	mSceneMgr->setSkyDome(true,"Examples/CloudySky",8,4);

	frameCounter = 0;


}
//-------------------------------------------------------------------------------------
bool RobotApp::frameRenderingQueued(const Ogre::FrameEvent& evt )
{
	++frameCounter;
	if(!RobotBase::frameRenderingQueued(evt))
		return false;


	world->stepSimulation(evt.timeSinceLastFrame);

	testSphere->update();
	if(testSphere->getYLoc() < 0)
	{

		btTransform t;
		Ogre::Vector3 newpos(mCamNode->convertLocalToWorldPosition(Ogre::Vector3::ZERO));
		t.setIdentity();
		t.setOrigin(btVector3(1830,550,2116));
		testSphere->body->setCenterOfMassTransform(t);
		testSphere->body->clearForces();
		testSphere->body->setMotionState(new btDefaultMotionState(t));
		int oldpowerlevel = powerLevel;
		powerLevel = 30;
		for(int i =0; i<35; ++i)
		{
			fire();
		}
		powerLevel = oldpowerlevel;
	}
	for(int i=0; i < objects.size(); ++i){

		//Ogre::SceneNode n;
		//n.getOrientation().zAxis()
		objects[i]->node->lookAt(testSphere->node->convertLocalToWorldPosition(Ogre::Vector3::ZERO),Ogre::SceneNode::TS_WORLD);
		Ogre::Quaternion quat2 = objects[i]->node->getOrientation();
		Ogre::Quaternion quat = objects[i]->node->convertLocalToWorldOrientation(quat2.IDENTITY);
		Ogre::Vector3 othervec = -quat.zAxis();
		btVector3 vec(othervec.x,othervec.y,othervec.z);
		objects[i]->body->applyImpulse(53.5*vec,vec);
		objects[i]->update();
		if(objects[i]->getYLoc() < 0)
		{
			delete objects[i];
			std::vector<PhysicsObject*>::iterator it = objects.begin();
			it += i;
			objects.erase(it);
		}
	}

    mTrayMgr->moveWidgetToTray(mInfoLabel, OgreBites::TL_TOP, 0);
    mInfoLabel->show();

    Ogre::Vector3 direction = mCamera->getDerivedDirection();

	std::stringstream ss;
	ss << "Number Of Objects " << objects.size() << " Power Level " << powerLevel <<
			" Number of Cubes per shot " << numPhysSteps;
	Ogre::DisplayString string = ss.str();
	mInfoLabel->setCaption(string);



	Ogre::Vector3 pos = mCamNode->convertLocalToWorldPosition(Ogre::Vector3::ZERO);
	pos.y += 3;
	speed -= mCamNode->convertWorldToLocalPosition(pos);

	Ogre::Terrain* terr = mTerrainGroup->
				getTerrain(0,0);

	//terr->dirty();
	//terr->update();


	if(frameCounter % 10000 == 0){
		terr->dirtyLightmap();
		terr->updateDerivedData();
	}

	Ogre::Real height = terr->getHeightAtWorldPosition(mCamNode->
			convertLocalToWorldPosition(Ogre::Vector3::ZERO));

	while(height > mCamNode->convertLocalToWorldPosition(Ogre::Vector3::ZERO).y - 180)
	{

		mCamNode->translate(0,.25,0,Ogre::Node::TS_WORLD);
	}




	return true;

}
//-------------------------------------------------------------------------------------
void RobotApp::fire(){

	//delete testSphere;
	//world->removeRigidBody(physicsBody);

	Ogre::Entity* sphereEnt = mSceneMgr->getEntity("sphere")->clone(sphereGenerator->generate());


	/*btTransform t;
	t.setIdentity();
	Ogre::Vector3 pos = mCamNode->getPosition();
	t.setOrigin(btVector3(pos.x,pos.y,pos.z));
	Ogre::Quaternion quat = mCamNode->getOrientation();
	t.setRotation(btQuaternion(quat.w,quat.x,quat.y,quat.z));
	btScalar mass = 1;
	btSphereShape* sphere = new btSphereShape(10);
	btVector3 inertia(0,0,0);
	sphere->calculateLocalInertia(mass,inertia);
	btMotionState* motion = new btDefaultMotionState(t);
	btRigidBody::btRigidBodyConstructionInfo info(mass,motion,sphere,inertia);
	btRigidBody* body = new btRigidBody(info);
	world->addRigidBody(body);*/

	btTransform t;
	t.setIdentity();
	Ogre::Vector3 pos = mCamNode->getPosition();
	t.setOrigin(btVector3(pos.x,pos.y,pos.z));
	Ogre::Quaternion quat = mCamNode->getOrientation();
	t.setRotation(btQuaternion(quat.w,quat.x,quat.y,quat.z));
	btScalar mass = 1;
	//btSphereShape* sphere = new btSphereShape(5);
	btBoxShape* box = new btBoxShape(btVector3(10,10,10));
	btVector3 inertia(0,0,0);
	box->calculateLocalInertia(mass,inertia);
	btMotionState* motion = new btDefaultMotionState(t);
	btRigidBody::btRigidBodyConstructionInfo info(mass,motion,box,inertia);
	btRigidBody* body = new btRigidBody(info);
	body->setFriction(.0000001);
	body->setRollingFriction(.0000001);
	world->addRigidBody(body);


	Ogre::Vector3 direction = mCamera->getDerivedDirection();


	btVector3 dir(direction.x,direction.y,direction.z);

	//physicsBody->setLinearVelocity(powerLevel*dir);
	body->setLinearVelocity(powerLevel*dir);
	//testSphere = new PhysicsObject(mSceneMgr,sphereEnt,physicsBody,.2);
	objects.push_back(new PhysicsObject(mSceneMgr,sphereEnt,body,.2,world));


}
//-------------------------------------------------------------------------------------
void RobotApp::bulletTerrain(Ogre::Terrain* pTerrain)
{
    float* terrainHeightData = pTerrain->getHeightData();
    Ogre::Vector3 terrainPosition = pTerrain->getPosition();

    float * pDataConvert= new float[pTerrain->getSize() *pTerrain->getSize()];
    for(int i=0;i<pTerrain->getSize();i++)
       memcpy(
                   pDataConvert+pTerrain->getSize() * i, // source
                   terrainHeightData + pTerrain->getSize() * (pTerrain->getSize()-i-1), // target
                   sizeof(float)*(pTerrain->getSize()) // size
                   );

    float metersBetweenVertices = pTerrain->getWorldSize()/(pTerrain->getSize()-1); //edit: fixed 0 -> 1 on 2010-08-13
    btVector3 localScaling(metersBetweenVertices, 1, metersBetweenVertices);


    btHeightfieldTerrainShape* groundShape = new btHeightfieldTerrainShape(
                pTerrain->getSize(),
                pTerrain->getSize(),
                pDataConvert,
                1/*ignore*/,
                pTerrain->getMinHeight(),
                pTerrain->getMaxHeight(),
                1,
                PHY_FLOAT,
                true);

    groundShape->setUseDiamondSubdivision(true);
    groundShape->setLocalScaling(localScaling);

    mGroundBody = new btRigidBody(0, new btDefaultMotionState(), groundShape);

    mGroundBody->getWorldTransform().setOrigin(
                btVector3(
                    terrainPosition.x,
                    terrainPosition.y + (pTerrain->getMaxHeight()-pTerrain->getMinHeight())/2,
                    terrainPosition.z));

    mGroundBody->getWorldTransform().setRotation(
                btQuaternion(
                    Ogre::Quaternion::IDENTITY.x,
                    Ogre::Quaternion::IDENTITY.y,
                    Ogre::Quaternion::IDENTITY.z,
                    Ogre::Quaternion::IDENTITY.w));

    world->addRigidBody(mGroundBody);
}
//-------------------------------------------------------------------------------------
bool RobotApp::mousePressed( const OIS::MouseEvent& evt, OIS::MouseButtonID id ){

	//Ogre::Light *light = mSceneMgr->getLight("Light1");
	switch (id)
	{
	case OIS::MB_Left:
		for(int i=0; i<numPhysSteps; ++i)
		{
			fire();
		}
	    break;
	case OIS::MB_Right:
		for(int i=0; i<numPhysSteps*10; ++i)
		{
			fire();
		}
	    break;
	case OIS::MB_Middle:
		for(int i=0; i<numPhysSteps*35; ++i)
		{
			fire();
		}
	    break;
	default:
	    break;
	}
	return true;
}





















